<?php
namespace Kuoqi;
use Kuoqi\Core\Exception;
use Kuoqi\FileDriver\Mime;

/**
 * Class FileDriver.class
 * @package Kuoqi
 * User kuoqi
 * Datetime 2022/2/28 15:45
 * Remark 文件模块的核心部分
 */
class FileDriver{


    /**
     * @var string
     * User kuoqi
     * Datetime 2022/2/28 16:04
     * Remark 文件路径部分
     */
    protected $dirname = '';

    /**
     * @var string 文件文件名部分，包含后缀信息
     */
    protected $basename = '';

    protected $handle = null;//准备的handle为文件打开准备的

    /**
     * @var bool
     * User kuoqi
     * Datetime 2022/2/28 16:08
     * Remark 判定文件是否ok
     */
    public $is_valied = false;

    /**
     * @var string
     * User kuoqi
     * Datetime 2022/2/28 16:08
     * Remark 如果文件不ok的前提下，保存提示信息
     */
    protected $errmsg = '';

    /**
     * @var string
     * User kuoqi
     * Datetime 2022/2/28 17:11
     * Remark 文件内容体系
     */
    protected $content = null;

    /**
     * 当前文件的mime
     * @var string
     * User kuoqi
     * Datetime 2022/3/25 16:21
     * Remark
     */
    protected $mime = '';


    /**
     * @var string
     * User kuoqi
     * Datetime 2022/2/28 17:21
     * Remark 安全代码，记录到文件头
     */
    public $safe_code = '<?php if(!defined("SAFE_CODE")){header("HTTP/1.0 404 Not Found");die();}?>';


    /**
     * FileDriver constructor. 初始化 ，输入文件名进行操作
     * @param string $fname 文件名，如果一个参数的情况，这个应输入完整的文件名
     * @param null $extbasename 如果本参数不为null的情况下，$fname变更为路径信息，本参数传递为文件名称，包含后缀的部分
     */
    public function __construct($fname = '',$extbasename = null){
        if($fname){
            if($extbasename===null){
                $this->basename = basename($fname);
                $this->dirname = dirname($fname);
            }else{
                $this->basename = $extbasename;
                $this->dirname = $fname;
            }
        }
    }

    /**
     * 获取完整路径
     * @return string
     */
    public function getFullfile(){
        return Params::$APP_ROOT .$this->dirname.'/'.$this->basename;
    }

    /**
     * 根据链接地址获取内容区域
     * @param $link
     * @param array $options
     */
    public function getContentByLink($link,$options = array()){
        $this->basename = basename($link);
        $this->content = @file_get_contents($link);
        foreach ($options as $k=>$v){
            $this->$k = $v;
        }
    }

    /**
     * 获取文件绝对路径
     * @return string
     */
    public function getFile(){
        return $this->dirname.'/'.$this->basename;
    }


    /**
     * 判定文件是否存在
     * @return bool
     */
    public function exist(){
        if($this->basename){
            $exist = file_exists($this->getFullfile());
            return $exist;
        }else{
            $this->errmsg = 'FileDriver:文件名不存在';
            $this->is_valied = false;
            return false;
        }
    }

    /**
     * 获取url信息
     * @return mixed
     */
    public function getUrl(){
        return str_replace(Params::$DS,'/',$this->dirname.'/'.$this->basename);
    }

    /**
     * 获取完整的文件路径
     * @return string
     */
    public function getFullpath(){
        return Params::$APP_ROOT .$this->dirname;
    }

    /**
     * 获取文件路径
     * @return string
     */
    public function getPath(){
        return $this->dirname;
    }

    /**
     * 获取文件名称
     * @return null|string
     */
    public function getFilename(){
        return $this->basename;
    }

    /**
     * 获取文件名中基础的部分，除去文件ext后缀的部分
     * createby kuoqi at 2022/8/29 10:46
     * @param bool $hasExt 默认有后缀信息，为true等效于getFilename，为false时候，为去除后缀信息
     * @return bool|null|string
     */
    public function getBasename($hasExt = true){
        if($hasExt){
            return $this->basename;
        }else{
            //尝试获取当前文件是否有ext
            return substr($this->basename,0,strripos($this->basename,'.'));
        }
    }

    /**
     * 获取文件内容
     * @return mixed|null|string
     */
    public function getContent($isCache = true){
        if($isCache&&$this->content){
            //如果判定有数据信息，直接返回对应数据信息
            return $this->content;
        }
        $fname = $this->getFullfile();
        if(is_file($fname)&&is_readable($fname)){
            $this->is_valied = true;
            return $this->content = str_replace($this->safe_code,'',file_get_contents($fname));
        }else{
            $this->errmsg = 'file '.$this->getFile().' can not exise or readabled';
            $this->is_valied = false;
            return null;
        }
    }

    /**
     * 获取文件md5
     * @return string
     */
    public function getMd5(){
        if(!$this->content){
            $this->getContent();
        }
        return md5($this->content);
    }

    /**
     * 获取文件size
     * @return int
     */
    public function getSize(){
        if(!$this->content){
            $this->getContent();
        }
        return strlen($this->content);
    }

    /**
     * 配置文件内容
     * @param $stream
     */
    public function setContent($stream){
        $this->content = $stream;
    }

    /**
     * 清理掉已有的数据信息
     */
    public function clearContent(){
        $this->content = null;//清理掉已有的数据信息
    }

    /**
     * 文件中增加一行代码
     * @param $code
     * @return bool
     */
    public function addLine($code){
        $handle = fopen($this->getFullfile(),'a+');
        if(!$handle){
            return false;
        }
        fwrite($handle,$code."\n");
        fclose($handle);
        return true;
    }

    //内容追加
    public function addTo($code){
        $handle = fopen($this->getFullfile(),'a+');
        if(!$handle){
            return false;
        }
        fwrite($handle,$code);
        fclose($handle);
        return true;
    }

    /**
     * 打开文件对象
     * @param string $mod
     * @return bool|null|resource
     */
    public function fopen($mod = 'a+'){
        $this->handle = @fopen($this->getFullfile(),$mod);
        if($this->handle){
            return $this->handle;
        }else{
            $this->is_valied = false;
            $this->errmsg = '打开文件失败:'.$this->getFile();
            return false;
        }
    }

    /**
     * 关闭文件对象
     */
    public function fclose(){
        if($this->handle){
            fclose($this->handle);
            $this->handle = null;
        }
    }

    /**
     * 文件对象的内容写入
     * @param $stream
     * @return bool|int
     */
    public function fwrite($stream){
        if($this->handle){
            return fwrite($this->handle,$stream);
        }else{
            return false;
        }
    }


    //清空文本
    public function clear(){
        return file_put_contents ($this->getFullfile(),'');
    }

    /**
     * 检测路径并创建
     * @return bool
     */
    public function checkPath(){
        if(!(is_dir($this->getFullpath())||@mkdir($this->getFullpath(),0777,true))){
            $this->is_valied = false;
            $this->errmsg = 'dir[${APP_ROOT}'.$this->getPath().'] not writable';
            return false;
        }
        return true;
    }

    /**
     * 执行文件保存方法
     * @return bool
     */
    public function save(){
        if(!$this->checkPath()){
            //将错误信息交给checkPath处理
            return false;
        }
        if(@file_put_contents($this->getFullfile(),$this->safe_code.$this->content)===false){
            //写入数据失败，请检查文件权限
            $this->is_valied = false;
            $this->errmsg = 'file [${APP_ROOT}'.$this->getFile().'] not writable';
            return false;
        }
        return true;
    }

    /**
     * 判定文件信息是否有效
     * @return bool
     */
    public function isValied(){
        return $this->is_valied;
    }

    /**
     * 配置basename
     * @param $basename
     */
    public function setBasename($basename){
        $this->basename = $basename;
    }

    /**
     * 配置dirname
     * @param $dirname
     */
    public function setDirname($dirname){
        $this->dirname = $dirname;
    }

    /**
     * 获取dirname的相关信息
     * createby kuoqi at 2022/8/29 10:53
     * @return string
     */
    public function getDirname(){
        return $this->dirname;
    }

    /**
     * 以php的方式阅读文件并返回
     * @param $args
     * @param bool $isOnce
     * @return bool|string
     */
    public function readWithphp($args,$isOnce = false){
        $file = $this->getFullfile();
        ob_start();
        foreach($args as $k=>$v){$$k=$v;}
        if(is_file($file)){
            $isOnce?include_once($file):include($file);
            $this->is_valied = true;
        }else{
            $this->is_valied = false;
            $this->errmsg = $file.' is non-existes';
            return false;
        }
        return ob_get_clean();
    }

    /**
     * include加载文件
     * @param bool $isOnce
     * @return bool|mixed
     */
    public function includeFile($isOnce = true){
        $file = $this->getFullfile();
        if(is_file($file)){
            return $isOnce?include_once($file):include($file);
        }else{
            $this->is_valied = false;
            $this->errmsg = '文件夹['.$this->getPath().']不可写,请检查';
            return false;
        }
    }

    /**
     * 删除某个文件
     * @return bool 删除文件
     */
    public function delete(){
        return @unlink($this->getFullfile());
    }

    /**
     * 获取当前文件结尾
     * @return string
     */
    public function getExt(){
        //尝试获取当前文件是否有ext
        $ext = strtolower(strrchr($this->getFilename(),'.'));
        return $ext;
    }

    /**
     * 获取文件mime
     * @return mixed|null|string
     * User kuoqi
     * Datetime 2022/3/25 16:22
     * Remark
     */
    public function getMime(){
        if($this->mime){
            return $this->mime;
        }
        $ext = strrchr($this->getFilename(),'.');
        return $this->mime = Mime::ext2mime($ext);
    }

    /**
     * 以文件形式输出
     */
    public function flushTofile(){
        $stream = $this->getContent();
        header('content-type:'.$this->getMime());
        die($stream);
    }


    /**
     * 获取异常信息
     * @return string
     */
    public function getError(){
        return $this->errmsg;
    }

    //获取文件列表
    public function getList(){
        $list = @scandir($this->getFullpath());
        if(!$list){
            $this->is_valied = false;
            $this->errmsg = '文件路径不存在：'.$this->getPath();
            return false;
        }
        return $list;
    }

    /**
     * 清空路径及下面的所有文件及文件夹
     */
    public function clearPath(){
        if(!($list = $this->getList())){ return false;}
        $file = clone $this;
        $isempty = true;
        foreach ($list as $item){
            if(in_array($item,array('.','..'))){ continue;}
            if(is_dir($this->getFullpath().'/'.$item)){
                $file->setDirname($this->getPath().'/'.$item);
                $file->clearPath();
            }else{
                $file->setBasename($item);
                $file->delete();
            }
            $isempty = false;
        }
        if($isempty){
            if(!@rmdir($this->getFullpath())){
                $this->is_valied = false;
                $this->errmsg = '文件目录无法删除：'.$this->getPath();
                return false;
            }
        }
        return true;
    }

}